package com.wsoft.interceptor;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.wsoft.config.ResponseBodyContext;
import com.wsoft.constant.CommonConstant;
import com.wsoft.core.utils.LoginUserUtil;
import com.wsoft.core.utils.SqlAuthDisableHolder;
import com.wsoft.entity.SysLogEntity;
import com.wsoft.enums.ThreadNameEnum;
import com.wsoft.manager.SysLogManager;
import com.wsoft.utils.LogUtil;
import com.wsoft.utils.RequestUtil;
import com.wsoft.utils.ThreadPoolUtil;
import com.wsoft.vo.LogVO;
import com.wsoft.vo.LoginUserVO;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 系统日志拦截
 *
 * 在拦截器中调用 request.getReader() 或 request.getInputStream() 方法时，可能会遇到 getReader() has already been called for this request 的错误。这是因为 HttpServletRequest 的 getReader() 和 getInputStream() 方法只能被调用一次。
 * 为了解决这个问题，可以使用 HttpServletRequestWrapper 来包装请求，以便多次读取请求体。
 *
 * @Author z001
 * @Date 2024/11/18 18:19
 */
@Component
public class SysLogInterceptor implements HandlerInterceptor {
    @Autowired
    private ApplicationContext applicationContext;
    @Resource
    private SysLogManager sysLogManager;

    public SysLogInterceptor() {
        this.initThread();
    }
    /**
     * 线程池
     */
    private ThreadPoolTaskExecutor taskExecutor;

    /**
     * 初始化线程池
     */
    private void initThread() {
        // 创建一个自定义的 ThreadPoolTaskExecutor
        taskExecutor = ThreadPoolUtil.createThreadPoolTaskExecutor(
                5,
                10,
                1000,
                60,
                ThreadNameEnum.LOG.getName(),
                true,
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
    }


    @Override
    public boolean preHandle(@NotNull HttpServletRequest request,
                             @NotNull HttpServletResponse response,
                             @NotNull Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            LogVO logVO = new LogVO();
            //处理request 相关信息
            RequestUtil.handleLogInfo(logVO,request);

            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            logVO.setType(CommonConstant.LOG_TYPE_OPERATION);
            try {
                LoginUserVO loginUserVO = LoginUserUtil.getUserInfo().getLoginUserVO();
                logVO.setUserId(loginUserVO.getId().toString());
                logVO.setOrgId(loginUserVO.getLoginOrgId());
            }catch (Exception e){
                logVO.setUserId(null);
                logVO.setOrgId(null);
            }
            //获取存储日志相关信息
            LogUtil.preHandle(logVO,method,applicationContext);
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //手动关闭
        SqlAuthDisableHolder.clearStack();

        //获取日志map
        LogVO logVO = LogUtil.afterCompletion(ex);
        SysLogEntity sysLog =Convert.convert(SysLogEntity.class,logVO);
        if(ObjUtil.isNotEmpty(sysLog) && StrUtil.isNotBlank(sysLog.getUrl())){
            //保存日志
            taskExecutor.execute(() -> saveLog(sysLog));
        }
        ResponseBodyContext.clear();
    }

    /**
     * 保存日志
     * @param operateLogEntity
     */
    private void saveLog(SysLogEntity operateLogEntity) {
        sysLogManager.save(operateLogEntity);
    }


}
